home *** CD-ROM | disk | FTP | other *** search
/ By Popular Request 2.0 / By Popular Request 2.0 (Arsenal Computer).ISO / amiga_2 / curse210.lha / EXAMPLES / ODDS / FM.C < prev    next >
C/C++ Source or Header  |  1991-12-30  |  18KB  |  752 lines

  1. /*
  2.    
  3.    f m . c
  4.    
  5.    - curses-based binary file modifier
  6.    - default for terminfo curses
  7.    - compile with -DM_TERMCAP for termcap curses (an after-thought...)
  8.    - seems to work on files, devices, and directories(read only)
  9.    - developed for AT&T Unix 3.2.2 and  Xenix 2.3.2  (System V/386)
  10.    
  11.    Copyright (c) Tony Field  April-1990
  12.    
  13.    Permission is given to distribute the source and documentation files
  14.    and/or compiled binaries associated with fm as long as no monies are
  15.    exchanged for their use or distribution.
  16.    
  17.    No responsibility is taken for any errors on inaccuracies inherent
  18.    either to the comments or the code of this program, but, if reported
  19.    to me, an attempt will be made to fix them.
  20.    
  21.    Author:  Tony Field
  22.    tony@ajfcal
  23. */
  24.  
  25.  
  26. #ifdef AMIGA
  27. #define M_TERMCAP
  28. #endif /* AMIGA */
  29.  
  30. #include <stdio.h>
  31. #include <sys/types.h>
  32. #include <sys/stat.h>
  33. #include <fcntl.h>
  34. #include <ctype.h>
  35. #include <curses.h>
  36.  
  37. #ifndef AMIGA
  38. #define min(x,y) (((x) < (y)) ? (x) : (y))
  39. #define max(x,y) (((x) > (y)) ? (x) : (y))
  40. #endif /* AMIGA */
  41.  
  42. #define BSIZE 256      /* size of window onto file */
  43. #define FIRSTL 3      /*  first screen line    */
  44. #define FIRSTH 6      /* first hex column   */
  45. #define FIRSTA 58      /* first alpha column   */
  46.  
  47. #define FWD  ('F' & 0x1f)   /* next page     */
  48. #define BAK  ('B' & 0x1f)   /*  previous page    */
  49.  
  50. #define DON  ('d' & 0x1f)   /* done/quit     */
  51. #define ASC  ('a' & 0x1f)   /* ascii side     */
  52. #define HEX  ('h' & 0x1f)   /* hex side     */
  53. #define UND  ('u' & 0x1f)   /* undo current changes  */
  54. #define JMP  ('g' & 0x1f)   /* goto address    */
  55. #define STX  ('t' & 0x1f)   /* text (ascii) search   */
  56. #define SHX  ('x' & 0x1f)   /* number (hex) search   */
  57. #define PRI  ('p' & 0x1f)   /* print to file    */
  58.  
  59. char hex[] = "0123456789abcdef";
  60. int  byte_pos;     /* current byte editing in buffer */
  61. int  maybe_dirty;    /* user may have changed data  */
  62.  
  63. main (argc, argv)
  64.      int  argc;
  65.      char *argv[];
  66. {
  67.   int  fp;
  68.   char fname[200];
  69.   char fbuf[BSIZE];  /* file i/o buffer   */
  70.   char wbuf[BSIZE];  /* work buffer copy of fbuf*/
  71.   long fpos, fsearch;  /* current file position */
  72.   long max_fpos;   /* end of file byte  */
  73.   int  nbytes;    /* number of bytes read */
  74.   int  key, yesno;
  75.   char resp[100];   /* user response   */
  76.   char stext[100];   /* last ascii search string*/
  77.   char shex[100];   /* last hex search string */
  78.   char hexstring[50];
  79.   long search ();   /* search function   */
  80.   int  nn;
  81.   struct stat statbuf;
  82.   int  rwmode;    /* file r/w or r/o mode */
  83.   FILE *pfile = NULL;  /* print file    */
  84.   
  85.   if (argc == 1)
  86.     usage ();
  87.   
  88.   /* determine size - for directories or regular files.  */
  89.   
  90.   strcpy (fname, argv[1]);
  91.   stat (fname, &statbuf);
  92.   if ((statbuf.st_mode & S_IFMT) == S_IFDIR
  93.       ||  (statbuf.st_mode & S_IFMT) == S_IFREG)
  94.     max_fpos = (long) statbuf.st_size;
  95.   else
  96.     max_fpos = 1999999999;  /* infinity??? */
  97.   
  98.   if ((fp = open (fname, O_RDWR)) == -1)
  99.     { if ((fp = open (fname, O_RDONLY)) == -1)
  100.     { printf ("file %s is unavailable\n", fname);
  101.       exit (1);
  102.     }
  103.       rwmode = 0;     /* read only */
  104.     }
  105.   else
  106.     rwmode = 1;     /* read/write */
  107.   
  108.   initscr ();
  109.   erase ();
  110.   noecho ();
  111. #ifdef M_TERMCAP
  112.   raw ();
  113.   nl();
  114. #endif
  115.   keypad (stdscr, TRUE);
  116.   key = 0;
  117.   fpos = 0;
  118.   byte_pos = 0;
  119.   stext[0] = '\0';
  120.   shex[0] = '\0';
  121.   
  122.   while (key != DON)
  123.     {
  124.       switch (key)
  125.     {
  126.     case FWD:        /* next screen load  */
  127.       if (fpos + BSIZE < max_fpos)
  128.         { fpos += BSIZE;
  129.         }
  130.       break;
  131.       
  132.     case BAK:        /* previous screen load */
  133.       fpos -= BSIZE;
  134.       if (fpos < 0L)
  135.         { fpos = 0L;
  136.           byte_pos = 0;
  137.         }
  138.       break;
  139.       
  140.     case JMP:        /* goto byte address  */
  141.       echo ();
  142.       mvwaddstr (stdscr, 1, 20, "jump address = ");
  143.       wclrtoeol (stdscr);
  144. #ifdef M_TERMCAP
  145.       wrefresh (stdscr);
  146. #endif
  147.       wgetstr (stdscr, resp);
  148.       sscanf (resp, "%lx", &fpos);
  149.       if (fpos < 0)
  150.         fpos = 0;
  151.       else if (fpos > max_fpos)
  152.         fpos = max_fpos;
  153.       byte_pos = fpos & 0x0ff;
  154.       fpos &= ~((long) BSIZE - 1);
  155.       noecho ();
  156.       break;
  157.       
  158.     case STX:        /* search ascii text  */
  159.       echo ();
  160.       if (*stext)
  161.         mvwprintw (stdscr, 2, 20, "( / = use '%s')", stext);
  162.       mvwaddstr (stdscr, 1, 20, "ascii search = ");
  163.       wclrtoeol (stdscr);
  164. #ifdef M_TERMCAP
  165.       wrefresh (stdscr);
  166. #endif
  167.       wgetstr (stdscr, resp);
  168.       noecho ();
  169.       if (resp[0] == '/'  &&  resp[1] == '\0')
  170.         strcpy (resp, stext);
  171.       else
  172.         if (*resp)
  173.           strcpy (stext, resp);
  174.       if ((fsearch = search (fp, resp, strlen (resp), fpos + byte_pos + 1)) != -1)
  175.         { fpos = fsearch;
  176.           byte_pos = fpos & 0x0ff;
  177.           fpos &= ~((long) BSIZE - 1);
  178.         }
  179.       break;
  180.       
  181.     case SHX:        /* search hex string  */
  182.       echo ();
  183.       if (*shex)
  184.         mvwprintw (stdscr, 2, 20, "( / = use '%s')", shex);
  185.       mvwaddstr (stdscr, 1, 20, "hex search = ");
  186.       wclrtoeol (stdscr);
  187. #ifdef M_TERMCAP
  188.       wrefresh (stdscr);
  189. #endif
  190.       wgetstr (stdscr, resp);
  191.       if (resp[0] == '/'  &&  resp[1] == '\0')
  192.         strcpy (resp, shex);
  193.       else
  194.         if (*resp)
  195.           strcpy (shex, resp);
  196.       nn = cvthex (resp, hexstring);
  197.       noecho ();
  198.       if ((fsearch = search (fp, hexstring, nn, fpos + byte_pos + 1)) != -1)
  199.         { fpos = fsearch;
  200.           byte_pos = fpos & 0x0ff;
  201.           fpos &= ~((long) BSIZE - 1);
  202.         }
  203.       break;
  204.       
  205.     case PRI:
  206.       if (pfile == NULL)
  207.         {
  208.           echo ();
  209.           mvwaddstr (stdscr, 1, 20, "print file = ");
  210.           wclrtoeol (stdscr);
  211. #ifdef M_TERMCAP
  212.           wrefresh (stdscr);
  213. #endif
  214.           wgetstr (stdscr, resp);
  215.           if (resp[0] == '\0')
  216.         { noecho ();
  217.           break;
  218.         }
  219.           if ((pfile = fopen (resp, "a+")) == NULL)
  220.         { noecho ();
  221.           break;
  222.         }
  223.         }
  224.       else
  225.         echo ();
  226.       
  227.       mvwaddstr (stdscr, 1, 20, "print comment = ");
  228.       wclrtoeol (stdscr);
  229. #ifdef M_TERMCAP
  230.       wrefresh (stdscr);
  231. #endif
  232.       wgetstr (stdscr, resp);
  233.       noecho ();
  234.       printit (pfile, resp, fname, fpos + byte_pos, wbuf, nbytes, rwmode);
  235.       break;  
  236.       
  237.     case UND:        /* undo current changes */
  238.       break;
  239.       
  240.     default: ;
  241.     }
  242.       
  243.       /* get a 256 byte block of file.  copy to a working buffer (wbuf) */
  244.       
  245.       lseek (fp, fpos, 0);
  246.       nbytes = read (fp, fbuf, BSIZE);
  247.       memcpy (wbuf, fbuf, nbytes);
  248.       maybe_dirty = 0;
  249.       display (fname, fpos, wbuf, nbytes, rwmode);
  250.       wrefresh (stdscr);
  251.       
  252.       /* allow user to modify file  */
  253.       
  254.       if ((key = edit (wbuf, nbytes, fpos, rwmode)) != UND)
  255.     { if (rwmode  &&  maybe_dirty  &&  memcmp (fbuf, wbuf, BSIZE))
  256.         {
  257.           echo ();
  258.           mvwaddstr (stdscr, 1, 20, "apply changes (y/n): ");
  259. #ifdef M_TERMCAP
  260.           wrefresh (stdscr);
  261. #endif
  262.           yesno = wgetch (stdscr);
  263.           noecho ();
  264.           if (tolower (yesno) == 'y')
  265.         { lseek (fp, fpos, 0);
  266.           write (fp, wbuf, nbytes);
  267.         }
  268.           else
  269.         memcpy (wbuf, fbuf, nbytes);  /* in case PRInt */
  270.         }
  271.     }
  272.     }
  273. #ifdef M_TERMCAP
  274.   wrefresh (stdscr);
  275. #endif
  276.   if (pfile)
  277.     fclose (pfile);
  278.   close (fp);
  279.   wmove (stdscr, LINES-1, 0);
  280.   wclrtobot (stdscr);
  281.   refresh ();
  282.   noraw ();
  283.   echo ();
  284.   endwin ();
  285.   exit (0);
  286. }
  287.  
  288. /************************************************************************
  289.  * display ()               *
  290.  * display a 256 byte block of file with hex on left and alpha   *
  291.  * on right side.              *
  292.  ************************************************************************/
  293.  
  294. display (fname, fpos, wbuf, nbytes, rwmode)
  295.      char *fname;    /* file name        */
  296.      long fpos;    /* byte # within the file     */
  297.      char wbuf[];    /* working data buffer      */
  298.      int  nbytes;    /* number of bytes in wbuf     */
  299.      int  rwmode;    /* read/write or readonly     */
  300. { int  i, j, k, m;
  301.   char hbuf[2];  /* working buf to convert byte to hex  */
  302.   
  303.   erase ();
  304.   wmove (stdscr, 0,0);
  305.   wprintw (stdscr, "File: %s    %s\nByte: %lx", 
  306.        fname, rwmode ? "\0" : "(read only)", fpos);
  307.   
  308.   for (i = 0, j = FIRSTL;  i < nbytes;  i += 16, j++)
  309.     {
  310.       /* display hex side  */
  311.       
  312.       wmove (stdscr, j, 0);
  313.       wprintw (stdscr, "%02x", i);
  314.       wmove (stdscr, j, FIRSTH);
  315.       for (k = 0, m = i; k < 48  &&  m < nbytes;  k += 3, m++)
  316.     { ttox (hbuf, wbuf[m]);
  317.       waddch (stdscr, hbuf[0]);
  318.       waddch (stdscr, hbuf[1]);
  319.       waddch (stdscr, ' ');
  320.     }
  321.       
  322.       /* display ascii side */
  323.       
  324.       wmove (stdscr, j, FIRSTA);
  325.       for (k = 0, m = i;  k < 16 &&  m < nbytes;  k++, m++)
  326.     waddch (stdscr, ttoa (wbuf[m]));
  327.       
  328.     }
  329.   
  330.   /* bottom annotation */
  331.   
  332.   for (i = 0;  i < 16;  i++)
  333.     { wmove (stdscr, j + 1, i * 3 + FIRSTH);
  334.       waddch (stdscr, hex[i]);
  335.     }
  336.   wmove (stdscr, j + 1, FIRSTA);
  337.   waddstr (stdscr, "0123456789abcdef");
  338.   wmove (stdscr, 22, 0);
  339.   waddstr (stdscr, " ^f/pgdn=forward   ^h/f1=hex    ^x/f3=find hex    ^u/f5=undo   ^g/f7 =goto");
  340.   wmove (stdscr, 23, 0);
  341.   waddstr (stdscr, " ^b/pgup=backward  ^a/f2=ascii  ^t/f4=find ascii  ^p/f6=print  ^d/f10=done");
  342. }
  343.  
  344.  
  345. /************************************************************************
  346.  * printit ()               *
  347.  * display a 256 byte block of file with hex on left and alpha   *
  348.  * on right side to a print file.          *
  349.  ************************************************************************/
  350.  
  351. printit (pfile, comment, fname, fpos, wbuf, nbytes, rwmode)
  352.      FILE *pfile;    /* print file        */
  353.      char *comment;   /* comment note       */
  354.      char *fname;    /* file name        */
  355.      long fpos;    /* byte # within the file     */
  356.      char wbuf[];    /* working data buffer      */
  357.      int  nbytes;    /* number of bytes in wbuf     */
  358.      int  rwmode;    /* read/write or readonly     */
  359. { int  i, j, k, m;
  360.   char hbuf[2];  /* working buf to convert byte to hex  */
  361.   
  362.   fprintf (pfile, "File: %s    %s\nByte: %lx      %s\n", 
  363.        fname, rwmode ? "\0" : "(read only)", fpos, comment);
  364.   
  365.   for (i = 0, j = FIRSTL;  i < nbytes;  i += 16, j++)
  366.     {
  367.       /* display hex side  */
  368.       
  369.       fprintf (pfile, "%02x    ", i);
  370.       for (k = 0, m = i; k < 48  &&  m < nbytes;  k += 3, m++)
  371.     { ttox (hbuf, wbuf[m]);
  372.       fputc (hbuf[0], pfile);
  373.       fputc (hbuf[1], pfile);
  374.       fputc (' ', pfile);
  375.     }
  376.       /* display ascii side */
  377.       
  378.       fprintf (pfile, "   ");
  379.       for (k = 0, m = i;  k < 16 &&  m < nbytes;  k++, m++)
  380.     fputc (ttoa (wbuf[m]), pfile);
  381.       
  382.       fprintf (pfile, "\n");
  383.     }
  384.   /* bottom annotation */
  385.   
  386.   fprintf (pfile, "      "); 
  387.   for (i = 0;  i < 16;  i++)
  388.     fprintf (pfile, "%c  ", hex[i]);
  389.   fprintf (pfile, "   0123456789abcdef\n\n");
  390. }
  391.  
  392.  
  393. /************************************************************************
  394.  * edit ()                *
  395.  * allow user to modify the 256 byte block of text.     *
  396.  ************************************************************************/
  397.  
  398. edit (wbuf, nbytes, fpos, rwmode)
  399.      char *wbuf;    /* working data buffer      */
  400.      int  nbytes;    /* number of bytes in wbuf     */
  401.      long fpos;    /* byte # in file       */
  402.      int  rwmode;    /* 1 = r/w  0 = read only     */
  403. {
  404.   int key;     /* user entered this key    */
  405.   static int vmode = HEX;  /* remember hex/ascii side of screen */
  406.   int  x, y;    /* screen coords.      */
  407.   int  hexa, hexb, cbyte; 
  408.   int  reflected;   /* was a change really done   */
  409.   
  410.   if (byte_pos >= nbytes)  
  411.     byte_pos = nbytes - 1;
  412.   if (byte_pos < 0)
  413.     byte_pos = 0;
  414.   
  415.   while (1)
  416.     {
  417.       mvwprintw (stdscr, 1, 6, "%lx     ",  fpos + byte_pos);
  418.       whereis (byte_pos, vmode, &x, &y);
  419. #ifdef M_TERMCAP
  420.       wmove (stdscr, y, x);
  421.       wrefresh (stdscr);
  422. #endif
  423.       key = mvwgetch (stdscr, y, x);
  424.       
  425.       switch (key)   /* any user keys or function keys */
  426.     {
  427.     case FWD:
  428.     case KEY_NPAGE:
  429.       return (FWD);
  430.       break;
  431.       
  432.     case BAK:
  433.     case KEY_PPAGE:
  434.       return (BAK);
  435.       break;
  436.       
  437.     case KEY_UP:
  438.       if (byte_pos - 16 >= 0)
  439.         { byte_pos -= 16;
  440.         }
  441.       break;
  442.       
  443.     case KEY_DOWN:
  444.       if (byte_pos + 16 < nbytes)
  445.         { byte_pos += 16;
  446.         }
  447.       break;
  448.       
  449.     case KEY_LEFT:
  450.       if (byte_pos - 1 >= 0)
  451.         { byte_pos -= 1;
  452.         }
  453.       break;
  454.       
  455.     case '\n':
  456.     case KEY_RIGHT:
  457.       if (byte_pos + 1 < nbytes)
  458.         { byte_pos += 1;
  459.         }
  460.       break;
  461.       
  462.     case HEX:
  463.     case KEY_F(1):
  464.       vmode = HEX;
  465.       break;
  466.       
  467.     case ASC:
  468.     case KEY_F(2):
  469.       vmode = ASC;
  470.       break;
  471.       
  472.     case SHX:
  473.     case KEY_F(3):
  474.       vmode = HEX;
  475.       return (SHX);
  476.       break;
  477.       
  478.     case STX:
  479.     case KEY_F(4):
  480.       vmode = ASC;
  481.       return (STX);
  482.       break;
  483.       
  484.     case UND:
  485.     case KEY_F(5):
  486.     case KEY_UNDO:
  487.       return (UND);
  488.       break;
  489.       
  490.     case PRI:
  491.     case KEY_F(6):
  492.       return (PRI);
  493.       break;
  494.       
  495.     case JMP:
  496.     case KEY_F(7):
  497.       return (JMP);
  498.       break;
  499.       
  500.     case DON:
  501. #ifdef M_XENIX
  502.     case KEY_F(0):
  503. #else
  504.     case KEY_F(10):
  505. #endif
  506.       return (DON);
  507.       break;
  508.       
  509.     default: 
  510.       if (rwmode == 0)
  511.         break;
  512.       
  513.       /* user typed a key.   If in the ascii side,
  514.          allow the overstrike of the ascii value
  515.          If in the hex side, ensure user types
  516.          valid hex digits.
  517.          
  518.          reflect changes on both sides of the
  519.          display.
  520.          */
  521.       
  522.       reflected = 0;
  523.       if (vmode == ASC)
  524.         { if (isvalid (key))
  525.         { wattrset (stdscr, A_STANDOUT);
  526.           waddch (stdscr, key);
  527. #ifdef M_TERMCAP
  528.           wrefresh (stdscr);
  529. #endif
  530.           maybe_dirty = 1;
  531.           wbuf[byte_pos] = key;
  532.           reflect_change (byte_pos, HEX, wbuf);
  533.           reflected = 1;
  534.         }
  535.         }
  536.       else
  537.         { hexa = hexb = 0;
  538.           if (isxdigit (key))     /* 1st nibble  */
  539.         { wattrset (stdscr, A_STANDOUT);
  540.           waddch (stdscr, key);
  541. #ifdef M_TERMCAP
  542.           wrefresh (stdscr);
  543. #endif
  544.           hexa = key;
  545.           maybe_dirty = 1;
  546.           key = wgetch (stdscr);   /* 2nd nibble */
  547.           if (isxdigit (key))
  548.             { waddch (stdscr, key);
  549. #ifdef M_TERMCAP
  550.               wrefresh (stdscr);
  551. #endif
  552.               hexb = key;
  553.             }
  554.           reflected = 1;
  555.         }
  556.           if (hexa)
  557.         { wbuf[byte_pos] &= 0x0f;
  558.           wbuf[byte_pos] |= hexval (hexa) << 4;
  559.         }
  560.           if (hexb)
  561.         { wbuf[byte_pos] &= 0x0f0;
  562.           wbuf[byte_pos] |= hexval (hexb);
  563.         }
  564.           if (hexa || hexb)
  565.         reflect_change (byte_pos, ASC, wbuf);
  566.         }
  567.       if (byte_pos < BSIZE - 1  &&  (reflected || key == ' '))
  568.         { byte_pos++;
  569.         }
  570.       wattrset (stdscr, 0);
  571.       if (key == UND  ||  key == KEY_F(5))
  572.         return (UND);
  573.       break;
  574.     }
  575.     }
  576. }
  577.  
  578.  
  579. /************************************************************************
  580.  * reflect_changes ()             *
  581.  * ensure the byte is placed on the screen in the appropriate panel. *
  582.  ************************************************************************/
  583.  
  584. reflect_change (byte, vmode, wbuf)
  585.      int  byte;    /* relative byte number of wbuf to use  */
  586.      int  vmode;    /* HEX or ASC side.      */
  587.      char *wbuf;    /* working data buffer.     */
  588. { int  x,y;
  589.   char hbuf[2];
  590.   
  591.   whereis (byte, vmode, &x, &y);
  592.   wmove (stdscr, y, x);
  593.   if (vmode == ASC)
  594.     { waddch (stdscr, ttoa (wbuf[byte]));
  595.     }
  596.   else
  597.     { ttox (hbuf, wbuf[byte]);
  598.       waddch (stdscr, hbuf[0]);
  599.       waddch (stdscr, hbuf[1]);
  600.     }
  601. #ifdef M_TERMCAP
  602.   wrefresh (stdscr);
  603. #endif
  604. }
  605.  
  606. /************************************************************************
  607.  * hexval ()               *
  608.  * what is the 4-bit hex value of the letter provided?     *
  609.  ************************************************************************/
  610.  
  611. hexval (c)
  612.      int c;     /* 0123456789abcdef expected     */
  613. {
  614.   if (c >= '0'  &&  c <= '9')
  615.     return (c - '0');
  616.   else
  617.     return ((tolower (c) - 'a' + 10) & 0x0f);
  618. }
  619.  
  620.  
  621. /************************************************************************
  622.  * whereis ()               *
  623.  * given the relative byte position of a byte within the 256 byte  *
  624.  * buffer, compute the screen coordinates to be used.     *
  625.  ************************************************************************/
  626.  
  627. whereis (pos, which, x, y)
  628.      int  pos;    /* relative byte position in wbuf   */
  629.      int  which;    /* HEX or ASC side of display    */
  630.      int  *x, *y;    /* returned x,y coordinate on display  */
  631. {
  632.   if (which == ASC)
  633.     {
  634.       *x = pos % 16 + FIRSTA;
  635.       *y = pos / 16 + FIRSTL;
  636.     }
  637.   else
  638.     {
  639.       *x = (pos % 16) * 3 + FIRSTH;
  640.       *y = pos / 16 + FIRSTL;
  641.     }
  642. }
  643.  
  644. /************************************************************************
  645.  * ttox ()                *
  646.  * for a given character, return the 2 character hex equivalent.  *
  647.  ************************************************************************/
  648.  
  649. ttox (x, c)
  650.      char x[], c;
  651. {
  652.   x[1] = hex[c & 0x0f];
  653.   x[0] = hex[(c >> 4) & 0x0f];
  654. }
  655.  
  656. /************************************************************************
  657.  * ttoa ()                *
  658.  * return useful display equivalents for a character on the ASC side. *
  659.  ************************************************************************/
  660.  
  661. int ttoa (c)
  662.      char c;
  663. {
  664.   if (isvalid (c))
  665.     return ((int) c);
  666.   else
  667.     return ((int) '.');
  668. }
  669.  
  670. isvalid (c)
  671.      int  c;
  672. {
  673.   if (c >= ' '  &&  c < 127)
  674.     return (1);
  675.   else
  676.     return (0);
  677. }
  678.  
  679.  
  680. /************************************************************************
  681.  * search ()               *
  682.  * search the file for a string of bytes.  return the byte pointer  *
  683.  * if found.  If not found, return -1         *
  684.  ************************************************************************/
  685.  
  686. long search (fp, s, n, here)
  687.      int  fp;     /* file to search   */
  688.      char *s;     /* look for this string */
  689.      int  n;     /* length of s    */
  690.      long here;    /* current byte position */
  691. {
  692.   char buf[1000];
  693.   int  nn, i, j;
  694.   long ptr;
  695.   
  696.   ptr = here;
  697.   lseek (fp, ptr, 0);   /* position to current byte  */
  698.   
  699.   while ((nn = read (fp, buf, 1000)) != -1  &&  nn >= n)
  700.     { for (i = 0;  i < nn - n + 1;  i++)
  701.     { for (j = 0;  j < n;  j++)   /* test string match  */
  702.         if (s[j] != buf[i+j])
  703.           break;
  704.       if (j == n)
  705.         { return (ptr + i);    /* valid match found */
  706.         }
  707.     }
  708.       ptr += 1000L + (long) (1 - n);   /* with n-1 overlap */
  709.       lseek (fp, ptr, 0);
  710.     }
  711.   return (-1L);        /* no match found  */
  712. }
  713.  
  714.  
  715. /************************************************************************
  716.  * cvthex ()               *
  717.  * convert a string of hex 'characters' into bytes.     *
  718.  * attempt to allow blanks or not as user sees fit.     *
  719.  ************************************************************************/
  720.  
  721. cvthex (s, sx)
  722.      char *s;    /* input string eg:  c524fe     */
  723.      char *sx;   /* returned packed bytes.      */
  724. {
  725.   int  i, n;
  726.   int   c1, c2;
  727.   
  728.   n = 0;
  729.   while (*s)
  730.     { if (*s == ' ')
  731.     { s++;
  732.       continue;
  733.     }
  734.       c1 = hexval (*s++);
  735.       if (*s == ' ')
  736.     sx[n++] = c1;
  737.       else
  738.     { c2 = hexval (*s++);
  739.       sx[n++] = (c1 << 4) | c2;
  740.     }
  741.     }
  742.   
  743.   return (n);
  744. }
  745.  
  746.  
  747. usage ()
  748. {
  749.   printf ("Usage:      fm  file.name\n");
  750.   exit (0);
  751. }
  752.